package com.whynot.frame.basic.redis.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.Feature;
import com.whynot.frame.basic.redis.service.IRedisClient;
import com.whynot.frame.basic.util.SessionStreamUtils;
import com.whynot.frame.basic.util.StringUtils;
import com.google.common.collect.Lists;
import com.whynot.frame.basic.redis.service.IRedisClient;
import org.apache.commons.lang3.SerializationUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;


import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 描述: 缓存接口Redis的实现类:
 * 作者: chenyao
 * 日期: 2018-01-30.
 * 项目名称: curtain-whynot-basic-redis
 * 版本: 1.0.0
 * JDK: since 1.8
 */
@Repository("redisClient")
public class RedisClientImpl<T> implements IRedisClient {

    @Autowired
    RedisTemplate<String,Object> redisTemplate;

    /**
     * 写入Redis，当前实例对象T,同时转成Json格式
     * @param key
     * @param value
     * @param <T>
     */
    @Override
    public <T> void set(String key, T value) {
        redisTemplate.opsForValue().set(key, JSON.toJSONString(value));
    }

    /**
     * 写入Redis，字符串对象
     * @param key
     * @param value
     */
    @Override
    public void set(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 写入Redis，当前实例对象T, 同时转成Json格式，并设置失效时间
     * @param key
     * @param value
     * @param time 秒级别的
     * @param <T>
     */
    @Override
    public <T> void setex(String key, T value, int time) {
        redisTemplate.opsForValue().set(key, JSON.toJSONString(value), time, TimeUnit.SECONDS);
    }

    /**
     * 写入Redis，字符串对象，并设置失效时间
     * @param key
     * @param value
     * @param time 秒级别的
     */
    @Override
    public void setex(String key, String value, int time) {
        redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
    }

    /**
     * 写Redis，对象是Object任何对象
     * @param key
     * @param value
     */
    @Override
    public void setObject(final String key, final Object value) {
        redisTemplate.opsForValue().set(key, JSON.toJSONString(value));
    }

    /**
     * 写Redis，对象是Object任何对象，并设置失效时间
     * @param key
     * @param value
     * @param time 秒级别的
     */
    @Override
    public void setexObject(final String key, final Object value,
                            final int time) {
        redisTemplate.opsForValue().set(key, JSON.toJSONString(value), time, TimeUnit.SECONDS);
    }

    /**
     * 把对象当做二进制存储
     * @param key
     * @param value
     * @param time
     */
    @Override
    public void setexObjectByte(String key, Object value, int time) {
        redisTemplate.execute(new RedisCallback() {
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                connection.set(key.getBytes(), SerializationUtils.serialize((Serializable) value));
                if (time > 0) {
                    connection.expire(key.getBytes(), time);
                }
                return 1L;
            }
        });
    }


    /**
     * 使用TypeReference 构建 类型安全的异构容器
     * @param key
     * @param type
     * @param <T>
     * @return
     */
    public <T> T get(String key, TypeReference<T> type) {
        return JSON.parseObject(get(key), type, new Feature[0]);
    }

    /**
     * 获取Redis对象，字符串对象
     * @param key
     * @return
     */
    @Override
    public String get(String key) {
        Object value = redisTemplate.opsForValue().get(key);
        return value==null?"":value.toString();
    }

    /**
     * 获取Redis对象，当前实例对象T
     * @param key
     * @param clazz
     * @param <T>
     * @return
     */
    @Override
    public <T> T get(String key, Class<T> clazz) {
        Object obj = getObject(key);
        if(obj == null){
            return null;
        }
        return JSON.parseObject((String)redisTemplate.opsForValue().get(key), clazz);
    }

    /**
     * 获取Redis对象，对象是Object任何对象
     * @param key
     * @return
     */
    @Override
    public Object getObject(final String key) {
        Object obj = redisTemplate.opsForValue().get(key);
        return obj==null?null:JSON.parseObject(obj.toString());
    }

    /**
     * 把key转为对象，通过二进制转换。以免有些对象的属性没有get/set
     * @param key
     * @return
     */
    @Override
    public Object getObjectByte(String key) {
        return redisTemplate.execute(new RedisCallback() {
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                try {
                    byte[] b = connection.get(key.getBytes());
                    if(StringUtils.isEmpty(b)){
                        return null;
                    }
                    return SerializationUtils.deserialize(b);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    /**
     * 插入Map对象，是String类型的
     * @param keyValues
     */
    @Override
    public void mset(final Map<String, String> keyValues){
        redisTemplate.opsForValue().multiSet(keyValues);
    }

    /**
     * 获取List对象，是String类型的
     * @param keys
     */
    @Override
    public List<Object> mget(final String[] keys){
        return redisTemplate.opsForValue().multiGet(Lists.newArrayList(keys));
    }


    /**
     * 列表左入栈
     * @param key
     * @param value
     * @return
     */
    @Override
    public long lpush(final String key, final String value){
        return redisTemplate.opsForList().leftPush(key,value);
    }

    /**
     * 列表右入栈
     * @param key
     * @param value
     * @return
     */
    @Override
    public long rpush(final String key, String value){
        return redisTemplate.opsForList().rightPush(key, value);
    }

    /**
     * 列表左出栈
     * @param key
     * @return
     */
    @Override
    public String lpop(final String key){
        return (String)redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 列表右出栈
     * @param key
     */
    @Override
    public String rpop(final String key){
        return (String)redisTemplate.opsForList().rightPop(key);
    }

    /**
     * 获取列表范围数据
     * @param key
     */
    @Override
    public List<String> lrange(final String key, final long start,
                               final long end){
        List<Object> list = redisTemplate.opsForList().range(key, start, end);
        List<String> results = new ArrayList<String>();
        for(Object obj:list){
            results.add((String)obj);
        }
        return results;
    }



    /**
     * 插入Hash对象，单个插入
     * @param key
     * @param field
     * @param value
     */
    @Override
    public void hsetObject(final String key, final String field, final Object value) {
        redisTemplate.opsForHash().put(key, field, JSON.toJSONString(value));
    }

    /**
     * 获取Hash对象的值，根据key找到Hash，再根据field找到对应value
     * @param key
     * @param field
     * @return
     */
    @Override
    public Object hgetObject(final String key, final String field) {
        return redisTemplate.opsForHash().get(key, field);
    }

    /**
     * 根据key获取Hash对象
     * @param key
     * @return
     */
    @Override
    public Map<String, Object> hgetAllObjects(final String key) {
        HashOperations<String,String,Object> ho = redisTemplate.opsForHash();
        return ho.entries(key);
    }

    /**
     * 删除对应Hash的对应field
     * @param key
     * @param field
     */
    @Override
    public void hdelObject(final String key, final String field) {
        redisTemplate.opsForHash().delete(key, field);
    }


    /**
     * 存入set集合，将set按数组存入
     * @param key
     * @param member 可变参数，例如string[]
     */
    @Override
    public void sadd(final String key, final String... member) {
        redisTemplate.opsForSet().add(key, member);
    }

    /**
     * 获取set集合
     * @param key
     * @return
     */
    @Override
    public Set<Object> smembers(final String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 移除集合中多个value值
     * @param key
     * @param member
     * @return
     */
    @Override
    public Long removeSet(final String key,final String... member){
        return redisTemplate.opsForSet().remove(key,member);
    }

    /**
     * 移除对象
     * @param key
     */
    @Override
    public void remove(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 设置key的失效时间
     * @param key
     * @param time 秒级别的
     */
    @Override
    public void expire(final String key, final int time) {
        redisTemplate.expire(key, time, TimeUnit.SECONDS);
    }

    /**
     * 获取模糊匹配的key
     * @param pattern
     * @return
     */
    @Override
    public Set<String> getKeys(final String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * 获取所有key
     * @return
     */
    @Override
    public Set<String> getAllKey() {
        return redisTemplate.keys("*");
    }

    /**
     * 获取有多少key
     * @return
     */
    @Override
    public Long dbSize() {
        return redisTemplate.getConnectionFactory().getConnection().dbSize();
    }

    /**
     * 清空当前Redis
     */
    @Override
    public void flushDB() {
        redisTemplate.getConnectionFactory().getConnection().flushDb();
    }

    /**
     * 关闭当前Redis连接
     */
    @Override
    public void shutDown() {
        redisTemplate.getConnectionFactory().getConnection().close();
    }

}
